/**********************************************************************************************************
** 驱动文件：	ptkt-RZY_RC100.c
** 驱动类型：	ptkt
** 设备名称：	普通空调监控
** 设备型号：	RZY_RC100
** 设备品牌：	融智云
** 设备协议：	Modbus-RTU
** 驱动描述：	普通空调远程控制器具有红外指令学习、来电自启动、空调运行状态检测（通过检测电流判断）等多种功能，学习原空调遥控器的的各种控制命令后，通过RS485接口可以实现远程开关机、设置温度、设置运行模式等多种功能。
** 生成日期：	2018-10-24 15:13:20
**********************************************************************************************************/
#include "Includes.h"


/**********************************************************************************************************
* 定义设备数据点变量结构
**********************************************************************************************************/
typedef struct{
	Var temperature;							//环境温度
	Var humidity;								//环境湿度
	Var onoff;									//开机/关机
	Var runningMode;							//运行模式
	Var setTemperature;							//设定温度
	Var runningState;							//运行状态
	Var current;								//实时电流
	Var currentThreshold;						//运行状态电流阀值
	Var commandLearnEnable;						//命令学习使能
	Var reserve;								//预留
}Data_Struct;									//定义变量结构


/**********************************************************************************************************
* 设备数据点变量属性声明
**********************************************************************************************************/
const static Var_Property_Def var_attr[] = {
	{
		FLOAT,
		0,
		READONLY,
		"temperature",
		"环境温度",
		"℃",
		"当前环境温度",
		1,
		0,
		"当前环境温度过高",
		"当前环境温度过低",
		35,
		0
	},
	{
		FLOAT,
		0,
		READONLY,
		"humidity",
		"环境湿度",
		"%",
		"当前环境湿度",
		1,
		0,
		"当前环境湿度过高",
		"当前环境湿度过低",
		75,
		15
	},
	{
		BOOLEAN,
		0,
		READWRITE,
		"onoff",
		"开机/关机",
		"",
		"空调开关控制：1-开机；0-关机",
		0,
		0,
		"",
		"",
		0,
		0
	},
	{
		INTEGER,
		0,
		READWRITE,
		"runningMode",
		"运行模式",
		"",
		"运行模式：0-自动；1-制冷；2-制热；3-抽湿",
		0,
		0,
		"",
		"",
		0,
		0
	},
	{
		INTEGER,
		0,
		READWRITE,
		"setTemperature",
		"设定温度",
		"℃",
		"设定空调温度，范围16~30℃",
		0,
		0,
		"",
		"",
		20,
		0
	},
	{
		OCTSTR,
		0,
		READONLY,
		"runningState",
		"运行状态",
		"",
		"空调当前运行状态：空调工作、空调停机",
		0,
		0,
		"空调停机",
		"",
		0,
		0
	},
	{
		FLOAT,
		0,
		READONLY,
		"current",
		"实时电流",
		"A",
		"空调实时运行电流，参考值用于空调工作和停机判断，非准确值",
		0,
		0,
		"电流过高",
		"空调停机",
		9999,
		0.05
	},
	{
		FLOAT,
		0,
		READWRITE,
		"currentThreshold",
		"运行状态电流阀值",
		"A",
		"空调工作和停机判断电流参考阀值",
		0,
		0,
		"",
		"",
		0,
		0
	},
	{
		BOOLEAN,
		0,
		READWRITE,
		"commandLearnEnable",
		"命令学习使能",
		"",
		"红外指令学习使能，1-启用；0-禁止",
		0,
		0,
		"",
		"",
		0,
		0
	},	
	{
		INTEGER,
		0,
		READONLY,
		"reserve",
		"预留",
		"",
		"预留参数",
		0,
		0,
		"",
		"",
		0,
		0
	}
};


/**********************************************************************************************************
* 函数名称： static void cmd_fun(short index, int addr, void *data, char *cmd, short *len)
* 函数说明： 生成获取传感器数据指令
* 输入参数： 指令索引，传感器地址，变量结构体指针,以及指令缓存区指针,指令长度
* 返回参数： 无
**********************************************************************************************************/
static void cmd_fun(short index, int addr, void *data, char *cmd, short *len)
{
	const unsigned char cmdnum[4] = {0,1,16,31};
	unsigned int		timestamp;
	
	Data_Struct 	*d = (Data_Struct*)data;
	if(d->setTemperature.value.inum>30) d->setTemperature.value.inum = 30;
	if(d->setTemperature.value.inum<16) d->setTemperature.value.inum = 16;

	d->currentThreshold.status = Normal;
	if(d->commandLearnEnable.status == NewSet || d->commandLearnEnable.status == Unknown){	//防止数据解析错乱
		d->commandLearnEnable.status = Normal;	
		return;
	}
	if(d->commandLearnEnable.value.inum){ //指令学习
		if(d->onoff.status==NewSet){
			cmd[0] = addr;
			cmd[1] = 0x05;
			cmd[2] = 0x00;
			cmd[3] = d->onoff.value.inum?0x00:0x3F;
			cmd[4] = 0xFE;
			cmd[5] = 0x01;
			*len = CRC16_Padding(cmd, 6);
		}
		else if(d->setTemperature.status==NewSet&&d->runningMode.value.inum>=1&&d->runningMode.value.inum<=3){
			cmd[0] = addr;
			cmd[1] = 0x05;
			cmd[2] = 0x00;
			cmd[3] = d->runningMode.value.inum ? cmdnum[d->runningMode.value.inum]+(d->setTemperature.value.inum-16) : 0x00;
			cmd[4] = 0xFE;
			cmd[5] = 0x01;
			*len = CRC16_Padding(cmd, 6);
		}
	}
	else{
		switch(index)
		{
			case 0:	//当温度过高时，空调运行电流过低，控制指令周期发送
				if(d->temperature.value.fnum>d->setTemperature.value.inum || (d->current.value.fnum < d->currentThreshold.value.fnum&&d->onoff.value.inum)){
					timestamp = GetCurrentTimestamp();
					if(d->reserve.value.inum<timestamp){
						d->reserve.value.inum	= timestamp+300;
						d->onoff.status			= NewSet;
						d->runningMode.status	= NewSet;
						d->setTemperature.status= NewSet;
					}
				}
				*len  = -1;	
				break;	
			case 1:	//进行开关机控制
				if(d->onoff.status){
					cmd[0] = addr;
					cmd[1] = 0x05;
					cmd[2] = 0x00;
					cmd[3] = d->onoff.value.inum?0x00:0x3F;
					cmd[4] = 0xFF;
					cmd[5] = 0x01;
					*len = CRC16_Padding(cmd, 6);				
				}
				else{
					*len  = -1;			
				}
				break;
			case 2:	//获取温湿度和电流	
				cmd[0] = addr;
				cmd[1] = 0x04;
				cmd[2] = 0x00;								
				cmd[3] = 0x00;
				cmd[4] = 0x00;	
				cmd[5] = 0x04;
				*len = CRC16_Padding(cmd, 6);
				break;
			case 3:	//获取最后的发送指令
				if(d->onoff.status){
					cmd[0] = addr;
					cmd[1] = 0x04;
					cmd[2] = 0x01;
					cmd[3] = 0x04;
					cmd[4] = 0x00;
					cmd[5] = 0x01;
					*len = CRC16_Padding(cmd, 6);
				}
				else{
					*len  = -1;			
				}
				break;
			case 4:	//指令调温和模式发送
				if(d->onoff.value.inum&&(d->runningMode.status||d->setTemperature.status)&&d->runningMode.value.inum>=0&&d->runningMode.value.inum<=3){
					cmd[0] = addr;
					cmd[1] = 0x05;
					cmd[2] = 0x00;
					cmd[3] = d->runningMode.value.inum ? cmdnum[d->runningMode.value.inum]+(d->setTemperature.value.inum-16) : 0x00;
					cmd[4] = 0xFF;
					cmd[5] = 0x01;
					*len = CRC16_Padding(cmd, 6);
				}
				else{
					*len  = -1;
				}
				break;
			case 5:	//获取最后的发送指令
				cmd[0] = addr;
				cmd[1] = 0x04;
				cmd[2] = 0x01;
				cmd[3] = 0x04;
				cmd[4] = 0x00;
				cmd[5] = 0x01;
				*len = CRC16_Padding(cmd, 6);
				break;
			default:
				break;
		}
	}

}


/**********************************************************************************************************
* 函数名称： static int parse_fun(short index, int addr, void *data, char *buf, short len)
* 函数说明： 解析传感器响应的数据，并写入数据库
* 输入参数： 解析索引,传感器地址，变量结构体指针，以及响应数据缓存取指针，响应数据长度
* 返回参数： 返回解析结果，返回0成功，返回1失败
**********************************************************************************************************/
static int parse_fun(short index, int addr, void *data, char *buf, short len)
{
	union{
		unsigned short 	dat16;
      	unsigned char  	dat8[2];
    }DAT16_8;						   //数据类型转换	
	const unsigned char cmdnum[4] = {0,1,16,31};
	unsigned char		code;
	Data_Struct	*d = (Data_Struct*)data;

	if(buf!=NULL && buf[0]==addr && len>0 && CRC16_Check(buf,len))
	{
		if(d->commandLearnEnable.status == NewSet){	//防止数据解析错乱
			d->commandLearnEnable.status = Normal;	
			return 0;
		}
		if(d->commandLearnEnable.value.inum){
			d->onoff.status				= Normal;
			d->runningMode.status		= Normal;
			d->setTemperature.status	= Normal;
		}
		else{
			switch(index)
			{
				case 2:
					DAT16_8.dat8[1] = buf[3];
					DAT16_8.dat8[0] = buf[4];
					d->temperature.value.fnum = DAT16_8.dat16*0.1;
					DAT16_8.dat8[1] = buf[5];
					DAT16_8.dat8[0] = buf[6];
					d->humidity.value.fnum = DAT16_8.dat16*0.1;
					DAT16_8.dat8[1] = buf[7];
					DAT16_8.dat8[0] = buf[8];
					d->current.value.fnum = DAT16_8.dat16*0.001;
					
					if(d->current.value.fnum > d->currentThreshold.value.fnum){
						d->runningState.value.str = "空调工作";
						Set_Var_Status(&d->runningState, 	Normal, NULL);	
					}
					else{
						d->runningState.value.str = "空调停机";
						Set_Var_Status(&d->runningState, 	Alarm, NULL);	
					}
					break;
					
				case 3:
					DAT16_8.dat8[1] = buf[3];
					DAT16_8.dat8[0] = buf[4];
					if(d->onoff.value.inum){
						code = 0x00;
						if(DAT16_8.dat16==code){
							d->onoff.status				= Normal;
							d->runningMode.status		= NewSet;
							d->setTemperature.status	= NewSet;								
						}
					}
					else{
						code = 0x3F;
						if(DAT16_8.dat16==code){
							d->onoff.status				= Normal;
							d->runningMode.status		= Normal;
							d->setTemperature.status	= Normal;	
						}						
					}
					break;		
				case 5:
					DAT16_8.dat8[1] = buf[3];
					DAT16_8.dat8[0] = buf[4];
					if(d->onoff.value.inum){
						code = d->runningMode.value.inum ? cmdnum[d->runningMode.value.inum]+(d->setTemperature.value.inum-16) : 0x00;	
						if(DAT16_8.dat16==code){
							d->onoff.status				= Normal;
							d->runningMode.status		= Normal;
							d->setTemperature.status	= Normal;
						}
						else{
							d->onoff.status				= Normal;
							d->runningMode.status		= NewSet;
							d->setTemperature.status	= NewSet;
						}						
					}
					else{
						code = 0x3F;
						if(DAT16_8.dat16==code){
							d->onoff.status				= Normal;
							d->runningMode.status		= Normal;
							d->setTemperature.status	= Normal;
						}
						else{
							d->onoff.status				= NewSet;
							d->runningMode.status		= Normal;
							d->setTemperature.status	= Normal;							
						}
					}
					break;
				default:
					break;
			}			
		}
		return 0;
	}
	return 1;
}


/**********************************************************************************************************
* 函数名称： void ptkt_RZY_RC100_Registration(void)
* 函数说明： 空调监控驱动注册
* 输入参数： 无
* 返回参数： 无
**********************************************************************************************************/
void ptkt_RZY_RC100_Registration(void)
{
	DeviceDriverRegistration(
		"ptkt",													//设备类型
		"普通空调监控",											//设备名称（导航栏默认显示名称）
		"融智云",												//设备品牌
		"RZY_RC100",											//设备型号
		"普通空调远程控制器具有红外指令学习、来电自启动、空调运行状态检测（通过检测电流判断）等多种功能，学习原空调遥控器的的各种控制命令后，通过RS485接口可以实现远程开关机、设置温度、设置运行模式等多种功能。",//驱动描述
		var_attr,												//变量属性声明
		sizeof(Data_Struct),									//变量结构体空间大小
		cmd_fun,												//发送指令生成函数
		parse_fun,												//数据解析函数
		800														//设备响应超时时间
	);
}

